/********************************************************************************
* Copyright (c) 2009, Matteo Pelati (matteo.pelati@gmail.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the <organization> nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Matteo Pelati ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**************************************************************************************/


#pragma once


#include <fstream>
#include <string>
#include <cstdlib>
#include <stdlib.h>
#include <stdio.h>
#include "dbxml/DbXml.hpp"
#include "dbXmlLib.h"

using namespace DbXml;
using namespace System;
using namespace System::Collections::Generic;


namespace BerkeleyDB 
{
	public ref class DbMemorySize
	{
	private:
		u_int32_t gbytes;
		u_int32_t bytes;
	public:
		DbMemorySize(u_int32_t gbytes, u_int32_t bytes)
		{
			this->gbytes = gbytes;
			this->bytes = bytes;
		}
		property u_int32_t GigaBytes
		{
			void set(u_int32_t value) {this->gbytes = value;}
			u_int32_t get() {return this->gbytes;}
		}
		property u_int32_t Bytes
		{
			void set(u_int32_t value) {this->bytes = value;}
			u_int32_t get() {return this->bytes;}
		}
	};

	public ref class DbCacheSettings
	{
	private:
		DbMemorySize^ memsz;
		int ncache;

	public:
		DbCacheSettings(DbMemorySize^ memsz, int ncache)
		{
			this->memsz = memsz;
			this->ncache = ncache;
		};

		property DbMemorySize^ Size
		{
			void set(DbMemorySize^ value) {this->memsz = value;}
			DbMemorySize^ get() {return this->memsz;}
		}
		property int NumberOfCaches
		{
			void set(int value) {this->ncache = value;}
			int get() {return this->ncache;}
		}
	};

	public enum class DbDeadlockDetectMode : u_int32_t
	{
		None = 0,
		Deafult = DB_LOCK_DEFAULT,
		Expire = DB_LOCK_EXPIRE,
		MaxLocks = DB_LOCK_MAXLOCKS,
		MaxWrite = DB_LOCK_MAXWRITE,
		MinLocks = DB_LOCK_MINLOCKS,
		MinWrite = DB_LOCK_MINWRITE,
		Oldest = DB_LOCK_OLDEST,
		Random = DB_LOCK_RANDOM,
		Youngest = DB_LOCK_YOUNGEST
	};

	[Flags] public enum class DbEnvInitOptions : u_int32_t
	{
		None = 0,
		InitCdb = DB_INIT_CDB,
		InitLock = DB_INIT_LOCK,
		InitLog = DB_INIT_LOG,
		InitMpool = DB_INIT_MPOOL,
		InitRep = DB_INIT_REP,
		InitTxn = DB_INIT_TXN,
		Recover = DB_RECOVER,
		RecoverFatal = DB_RECOVER_FATAL,
		UseEnviron = DB_USE_ENVIRON,
		UseEnvironRoot = DB_USE_ENVIRON_ROOT,
		Create = DB_CREATE,
		Lockdown = DB_LOCKDOWN,
		Private = DB_PRIVATE,
		Register = DB_REGISTER,
		SystemMem = DB_SYSTEM_MEM,
		Thread = DB_THREAD
	};

	[Flags] public enum class DbEnvFlags : u_int32_t
	{
		None = 0,
		AutoCommit = DB_AUTO_COMMIT,
		CdbAllDb = DB_CDB_ALLDB,
		DirectDb = DB_DIRECT_DB,
		DirectLog = DB_DIRECT_LOG,
		DsyncDb = DB_DSYNC_DB,
		DsyncLog = DB_DSYNC_LOG,
		LogAutoremove = DB_LOG_AUTOREMOVE,
		LogInMemory = DB_LOG_INMEMORY,
		Multiversion = DB_LOG_INMEMORY,
		NoLocking = DB_NOLOCKING,
		NoMap = DB_NOMMAP,
		NoPanic = DB_NOPANIC,
		Overwrite = DB_OVERWRITE,
		PanicEnvironment = DB_PANIC_ENVIRONMENT,
		RegionInit = DB_REGION_INIT,
		TimeNotGranted = DB_TIME_NOTGRANTED,
		TxnNoSync = DB_TXN_NOSYNC,
		TxnNowait = DB_TXN_NOWAIT,
		TxnSnapshot = DB_TXN_SNAPSHOT,
		TxnWriteNoSync = DB_TXN_WRITE_NOSYNC,
		YeldCpu = DB_YIELDCPU 
	};


	[Flags] public enum class DbEncryptionMethod  : u_int32_t
	{
		None = 0,
		Aes = DB_ENCRYPT_AES
	};


	public ref class DbEncryptionSettings
	{
	private:
		String^ pwd;
		DbEncryptionMethod flags;

	public:
		DbEncryptionSettings(String^ password, DbEncryptionMethod mode)
		{
			pwd = password;
			flags = mode;
		}

		property DbEncryptionMethod Flags
		{
			void set(DbEncryptionMethod value) {flags = value;}
			DbEncryptionMethod get() {return flags;}
		}
		property String^ Password
		{
			void set(String^ value) {pwd = value;}
			String^ get() {return pwd;}
		}
	};


	public ref class DbEnvironment :  public DbGenericObject
	{
	private:
		DbEnv* env; 
	internal:
		DbEnv* GetInnerObject()
		{
			return env;
		};
	public:
		~DbEnvironment()
		{
			GC::SuppressFinalize(this);
			this->!DbEnvironment();
		};
		!DbEnvironment()
		{
			delete env;
		};
	public:
		DbEnvironment() : DbGenericObject(nullptr, nullptr)
		{
			env = new DbEnv(0);
		}

		PROPERTY_UINT32_BOTH(MaxLockers,, _lk_max_lockers)
		PROPERTY_UINT32_BOTH(MaxLocks,, _lk_max_locks);
		PROPERTY_UINT32_BOTH(MaxLockedObjects,, _lk_max_objects);
		PROPERTY_INT_BOTH(MaxFileDescriptors,, _mp_max_openfd);
		PROPERTY_ENUM_BOTH(LockDetection,, _lk_detect, DbDeadlockDetectMode, u_int32_t);

		property DbCacheSettings^ CacheSize
		{
			DbCacheSettings^ get()
			{
				u_int32_t gbytes, bytes;
				int ncache;
	
				env->get_cachesize(&gbytes, &bytes, &ncache); 
				return gcnew DbCacheSettings(gcnew DbMemorySize(gbytes, bytes), ncache);
			}
			void set(DbCacheSettings^ value)
			{
				env->set_cachesize(value->Size->GigaBytes, value->Size->Bytes, value->NumberOfCaches);
			}
		}

		property IEnumerable<String^>^ DataDirectories
		{
			IEnumerable<String^>^ get() 
			{
				int idx = 0;
				ICollection<String^>^ strls = gcnew List<String^>();
				return strls;
			}
		}

		property DbEncryptionSettings^ EncryptionSettings
		{
			DbEncryptionSettings^ get()
			{
				u_int32_t out;
				env->get_encrypt_flags(&out);
				return gcnew DbEncryptionSettings(nullptr, (DbEncryptionMethod) out);
			}
			void set(DbEncryptionSettings^ value)
			{
				u_int32_t flags = (u_int32_t) value->Flags;
				char* pwd = CLR_MARSHAL_STRING(value->Password);
				env->set_encrypt(pwd, flags);
				CLR_MARSHAL_FREE(pwd);
			}
		}

		property unsigned int LockTimeout
		{
			unsigned int  get()
			{
				db_timeout_t retval = 0;
				env->get_timeout(&retval, DB_SET_LOCK_TIMEOUT);
				return retval;
			}
			void set(unsigned int value)
			{
				env->set_timeout(value, DB_SET_LOCK_TIMEOUT);
			}
		}

		property unsigned int  TransactionTimeout
		{
			unsigned int  get()
			{
				db_timeout_t retval = 0;
				env->get_timeout(&retval, DB_SET_TXN_TIMEOUT);
				return retval;
			}
			void set(unsigned int  value)
			{
				env->set_timeout(value, DB_SET_TXN_TIMEOUT);
			}
		}


		PROPERTY_UINT32_GET(Flags,, _flags);
		PROPERTY_UINT32_BOTH(LogBufferSize,, _lg_bsize);
		PROPERTY_STRING_BOTH(LogDirectory,, _lg_dir);
		PROPERTY_UINT32_BOTH(MaxLogFileSize,, _lg_max);
		PROPERTY_UINT32_BOTH(MaxLogRegion,, _lg_regionmax);
		PROPERTY_SIZE_BOTH(MemoryMapSize,, _mp_mmapsize);
		PROPERTY_UINT32_BOTH(ThreadCount,, _thread_count);
		PROPERTY_STRING_BOTH(TempDirectory,, _tmp_dir);
		PROPERTY_UINT32_BOTH(MaxActiveTransactions,, _tx_max);
		PROPERTY_STRING_GET(HomeDirectory,, _home);
		PROPERTY_ENUM_GET(OpenFlags,, _open_flags, DbEnvInitOptions, u_int32_t);
		PROPERTY_UINT32_BOTH(MutexAlignment, mutex_, _align);
		PROPERTY_UINT32_BOTH(MaxMutexCount, mutex_, _max);
		PROPERTY_UINT32_BOTH(MutexIncrement, mutex_, _increment);
		PROPERTY_UINT32_BOTH(MutexTasSpins, mutex_, _tas_spins);
		PROPERTY_INT_BOTH(ReplicationPriority, rep_, _priority);
		PROPERTY_INT_BOTH(ReplicationSites,rep_, _nsites);
		PROPERTY_MEMSIZE_BOTH(ReplicationMemoryLimit, rep_, _limit); 
		PROPERTY_MEMSIZE_BOTH(MaxCacheSize,, _cache_max);


		// Methods
		void Open(String^ home, DbEnvInitOptions flags);
		void AddDataDirectory(String^ value);
		void SetFlagsOnOff(u_int32_t flags, bool onoff);
		int DetectDeadlocks(DbDeadlockDetectMode mode);


	};





}
